/*
 * Title: WADS
 * Description: Web-based Auto flow Documents System
 * Copyright: 2007
 * Company: CoE
 * @author: Old developer team.
 * @version 1.1
 */

package wads.drawing;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphMetrics;
import java.awt.font.GlyphVector;
import java.awt.font.LineMetrics;
import java.awt.geom.Point2D;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.Vector;
import wads.app.DrawPanel;

public abstract class CaseShape implements Cloneable, Serializable {
  public int id;
  public static int idCount;

  public static final String DEFAULT_FONT_NAME = "Serif";
  public static final int DEFAULT_FONT_STYLE = Font.PLAIN;
  public static final int DEFAULT_FONT_SIZE = 10;

  private String currentFontName = DEFAULT_FONT_NAME;
  private int currentFontStyle = DEFAULT_FONT_STYLE;
  private int currentFontSize = DEFAULT_FONT_SIZE;

  protected Font DEFAULT_FONT = new Font(currentFontName, currentFontStyle, currentFontSize);
  protected Font currentFont = DEFAULT_FONT;
  
  private String stereoType;
  private String name;

  protected Color  color = Color.black;
  
  protected transient Vector<Shape> shapes;

  //2D Bounds of shape
  protected transient Shape bounds2D;

  //Flag to determine if shape is selected
  private boolean selected = false;

  //Note: This seems redundant as you may think that using
  //drawPanel.getGraphics()
  
  //will get the CORRECT graphics context. However, strangely, it is not.
  //So we always need the graphics context given by system in paintComponent
  //public abstract String getId();
  public abstract void draw(DrawPanel drawPanel, Graphics g);
  public abstract void draw(DrawPanel drawPanel);
  public abstract void resize (DrawPanel drawPanel, Point p);
  public abstract void resize (DrawPanel drawPanel, Point p, Cursor cursor);
  public abstract void setMoveAnchor (Point p);
  public abstract void move (DrawPanel drawPanel, Point p);
  public abstract void highlight (DrawPanel drawPanel);
  public abstract void highlight (Graphics g);
  public abstract void dehighlight (DrawPanel drawPanel);
  public abstract void setHandleCursor (DrawPanel drawPanel, Point p);
  public abstract Shape getScaledBounds(float scaleFactor);
  
  public CaseShape(){
    shapes = new Vector<Shape>();
    stereoType = "";
  }// end of CaseShape()

  /* The constructor that takes in size is rather confusing.
     Should consider removing it.
  */
  public CaseShape(int size){
    shapes = new Vector<Shape>(size);
    stereoType = "";
  }// end of CaseShape(int size)

  public void removeAllComponents(){
    shapes.removeAllElements();
  }// end of removeAllComponents()

  /*private void setComponent(Shape sp,int index){
    if (shapes.elementAt(index) != null)
      shapes.setElementAt(sp,index);
  }*/

  public void append(Shape sp){
    if (shapes == null)
      shapes = new Vector<Shape>();
    shapes.addElement(sp);
  }// end of append()

  /*private Vector getShapeComponents(){
    return shapes;
  }*/

  public void erase(DrawPanel drawPanel){
    //Dehighlight if currently highlighted
    if (isSelected()){
      deselect();
      dehighlight(drawPanel);
    }

    //Erase
    Graphics g = drawPanel.getGraphics();
    g.setXORMode(drawPanel.getBackgroundColor());
    draw(drawPanel);
    g.setPaintMode();
  }// end of erase()

  public void delete(DrawPanel drawPanel){
    Graphics g = drawPanel.getGraphics();

    //Dehighlight if currently highlighted
    if (isSelected()){
      deselect();
      dehighlight(drawPanel);
    }

    //Erase
    g.setXORMode(drawPanel.getBackgroundColor());
    draw(drawPanel);
    g.setPaintMode();
  }// end of delete()

  public boolean isWithinScaledBounds(Point p, float scaleFactor){
    if (bounds2D==null){
      //System.out.println("Bounds2D not defined.");
      return false;
    }

    //Scale the bounds
    Shape scaledBounds = getScaledBounds(scaleFactor);
    
    //Check if cursor is within this bounding rectangle
    return scaledBounds.contains(p);
  }// end of isWithinScaledBounds()
  
  protected void setDefaultRenderingHints(Graphics2D g2d){
    //Set rendering hints
    RenderingHints renderHints = new RenderingHints(
        RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_OFF);
    renderHints.put(RenderingHints.KEY_RENDERING,
        RenderingHints.VALUE_RENDER_SPEED);
    g2d.setRenderingHints(renderHints);
    
    renderHints.put(RenderingHints.KEY_DITHERING,
        RenderingHints.VALUE_DITHER_DISABLE);
    g2d.setRenderingHints(renderHints);
    /* Problem with FlexOr means removing this statement.
       - problem is length of identifier exceeds 31 characters
       - it did not cause a problem with it removed.
       renderHints.put(RenderingHints.KEY_INTERPOLATION,
         RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
       g2d.setRenderingHints(renderHints);
    */
  }// end of setDefaultRenderingHints()

  protected void draw(Graphics2D g2d){
    //Set the manner the graphics should be rendered
    setDefaultRenderingHints(g2d);
    
    int size = shapes.size();
    Shape tmp;

    for (int i=0; i<size; i++){
      tmp = (Shape)shapes.elementAt(i);
      if (tmp != null)
        g2d.draw(tmp);
    }
  }// end of draw()

  public void select(){
    selected = true;
  }

  public void deselect(){
    selected = false;
  }

  public boolean isSelected(){
    return selected;
  }

  public Color getColor(){ return color; }

  public void setColor(Color color){ this.color = color; }

    @Override
  public Object clone(){
    //connectedLinks references
    try{
      //Shallow copy CaseShape
      CaseShape shape = (CaseShape)super.clone();
      return shape;
    }catch(CloneNotSupportedException e) {
      return null;
    }
  }// end of clone()

  public void setShapeBounds2D(Shape b2d) {
    bounds2D = b2d;
  }

  public Shape getBounds2D(){
    return bounds2D;
  }

  //Test if a given coordinate is inside the boundary of the shape.
  public boolean contains(double x, double y){
    Shape bounds2d = getBounds2D();
    if (bounds2d != null)
      if (bounds2d.contains(x,y))
        return true;
    return false;
  }// end of contains()

  //Test if a given Point is inside the boundary of the shape.
  public boolean contains(Point2D p){
    Shape bounds2d = getBounds2D();
    if (bounds2d != null)
      if (bounds2d.contains(p))
        return true;
    return false;
  }

  private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    in.defaultReadObject();
    
    //Restoring shapes manually since shapes cannot be saved
    //Set it to a new vector so that the components can be added in
    //subclasses
    this.shapes = new Vector<Shape>();
  }// end of readObject()

  //added by Khoo Yong Bing, April, 2000
  public void setStereoType(String stereoType){
    this.stereoType = stereoType;
  }

  public String getStereoType(){
    return stereoType;
  }
  
  public void setName(String name){
    this.name = name;
  }

  public String getName() {
    return name;
  }
  
  protected int getFontHeight(Graphics2D g2d,Font f) {
    FontRenderContext frc = g2d.getFontRenderContext();
    LineMetrics lm = f.getLineMetrics("Ag",frc);
    return (int)Math.round(lm.getHeight());
  }
  
  protected int getStringWidth(Graphics2D g2d,Font f,String content) {
    FontRenderContext frc = g2d.getFontRenderContext();
    int width=0;
    
    for (int i=0; i<content.length(); i++) {
      GlyphVector gv = f.createGlyphVector(frc,
          new int[] {content.charAt(i)});
      GlyphMetrics gm = gv.getGlyphMetrics(0);
      width+=(int)Math.round(gm.getAdvance());
    }

    return width;
  }// end of getStringWidth()

    /**
     * @return the id
     */
    public int getId() {
        return id;
    }

    /**
     * @param id the id to set
     */
    public void setId(int id) {
        this.id = id;
    }
} // end of CaseShape class
